package com.abc.recycling.service.impl;

import com.abc.recycling.domain.statistics.ChartData;
import com.abc.recycling.domain.statistics.RankingData;
import com.abc.recycling.domain.statistics.RegionStatistics;
import com.abc.recycling.domain.statistics.StatisticsOverview;
import com.abc.recycling.mapper.OrderMapper;
import com.abc.recycling.mapper.UserMapper;
import com.abc.recycling.service.StatisticsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.*;
import com.abc.recycling.model.StatisticsData;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.ByteArrayOutputStream;
import com.abc.recycling.mapper.StationMapper;
import com.abc.recycling.repository.OrderRepository;
import com.abc.recycling.repository.UserRepository;
import java.io.IOException;
import java.math.BigDecimal;

@Service
@RequiredArgsConstructor
@Slf4j
public class StatisticsServiceImpl implements StatisticsService {

    private final OrderRepository orderRepository;
    private final UserRepository userRepository;
    private final OrderMapper orderMapper;
    private final UserMapper userMapper;
    private final StationMapper stationMapper;

    @Override
    public byte[] exportStatisticsReport(LocalDateTime startTime, LocalDateTime endTime) {
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("统计报表");
            
            Row headerRow = sheet.createRow(0);
            String[] headers = {"日期", "订单数", "总重量", "总收入", "平均评分"};
            for (int i = 0; i < headers.length; i++) {
                Cell cell = headerRow.createCell(i);
                cell.setCellValue(headers[i]);
            }
            
            List<Map<String, Object>> stats = orderRepository.getStatisticsReport(startTime, endTime);
            
            int rowNum = 1;
            for (Map<String, Object> stat : stats) {
                Row row = sheet.createRow(rowNum++);
                row.createCell(0).setCellValue(String.valueOf(stat.get("date")));
                row.createCell(1).setCellValue(((Number) stat.get("orderCount")).intValue());
                row.createCell(2).setCellValue(((Number) stat.get("totalWeight")).doubleValue());
                row.createCell(3).setCellValue(((Number) stat.get("totalIncome")).doubleValue());
                row.createCell(4).setCellValue(((Number) stat.get("averageRating")).doubleValue());
            }
            
            for (int i = 0; i < headers.length; i++) {
                sheet.autoSizeColumn(i);
            }
            
            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                workbook.write(outputStream);
                return outputStream.toByteArray();
            }
        } catch (IOException e) {
            log.error("导出统计报表失败", e);
            throw new RuntimeException("导出统计报表失败", e);
        }
    }

    @Override
    public StatisticsOverview getOverview(String timeRange) {
        StatisticsOverview overview = new StatisticsOverview();
        LocalDateTime startTime = getStartTimeByRange(timeRange);
        LocalDateTime endTime = LocalDateTime.now();

        Long totalOrders = orderRepository.countByCreateTimeBetween(startTime, endTime);
        Long totalUsers = userRepository.countActiveUsers();
        Double totalIncome = orderRepository.getTotalIncome();
        Double totalWeight = orderRepository.sumTotalWeight();

        overview.setTotalOrders(totalOrders.intValue());
        overview.setTotalUsers(totalUsers);
        overview.setTotalIncome(new BigDecimal(totalIncome));
        overview.setTotalWeight(totalWeight);

        LocalDateTime previousStartTime = startTime.minusDays(getDaysByRange(timeRange));
        Long previousOrders = orderRepository.countByCreateTimeBetween(previousStartTime, startTime);
        Long previousUsers = userRepository.countByCreateTimeBetween(previousStartTime, startTime);
        Double previousIncome = orderRepository.sumIncomeByDateRange(previousStartTime, startTime);
        Double previousWeight = orderRepository.sumWeightByDateRange(previousStartTime, startTime);

        overview.setOrderGrowthRate(calculateGrowthRate(previousOrders.doubleValue(), totalOrders.doubleValue()));
        overview.setUserGrowthRate(calculateGrowthRate(previousUsers.doubleValue(), totalUsers.doubleValue()));
        overview.setIncomeGrowthRate(calculateGrowthRate(previousIncome, totalIncome));
        overview.setWeightGrowthRate(calculateGrowthRate(previousWeight, totalWeight));

        return overview;
    }

    @Override
    public ChartData getTrendData(String timeRange) {
        LocalDateTime startTime = getStartTimeByRange(timeRange);
        LocalDateTime endTime = LocalDateTime.now();
        List<Map<String, Object>> data = orderRepository.getOrderTrend(startTime);
        
        ChartData chartData = new ChartData();
        List<String> labels = new ArrayList<>();
        List<Double> values = new ArrayList<>();
        
        for (Map<String, Object> item : data) {
            labels.add(item.get("date").toString());
            values.add(((Number) item.get("count")).doubleValue());
        }
        
        chartData.setLabels(labels);
        chartData.setValues(values);
        
        return chartData;
    }

    @Override
    public ChartData getTypeDistribution(String timeRange) {
        LocalDateTime startTime = getStartTimeByRange(timeRange);
        LocalDateTime endTime = LocalDateTime.now();
        List<Map<String, Object>> data = orderRepository.getItemDistributionByDateRange(startTime, endTime);
        
        ChartData chartData = new ChartData();
        List<String> labels = new ArrayList<>();
        List<Double> values = new ArrayList<>();
        
        for (Map<String, Object> item : data) {
            labels.add(item.get("type").toString());
            values.add(((Number) item.get("count")).doubleValue());
        }
        
        chartData.setLabels(labels);
        chartData.setValues(values);
        
        return chartData;
    }

    @Override
    public List<RankingData.StationRanking> getStationRanking(String timeRange, int limit) {
        LocalDateTime startTime = getStartTimeByRange(timeRange);
        LocalDateTime endTime = LocalDateTime.now();
        List<Map<String, Object>> data = orderRepository.getTopStations(startTime, endTime, limit);
        
        List<RankingData.StationRanking> result = new ArrayList<>();
        for (Map<String, Object> item : data) {
            RankingData.StationRanking ranking = new RankingData.StationRanking();
            ranking.setStationId(((Number) item.get("station_id")).longValue());
            ranking.setStationName((String) item.get("station_name"));
            ranking.setOrderCount(((Number) item.get("count")).longValue());
            ranking.setTotalWeight(new BigDecimal(((Number) item.get("total_weight")).toString()));
            ranking.setTotalIncome(new BigDecimal(((Number) item.get("total_income")).toString()));
            result.add(ranking);
        }
        return result;
    }

    @Override
    public List<RankingData.CollectorRanking> getCollectorRanking(String timeRange, int limit) {
        LocalDateTime startTime = getStartTimeByRange(timeRange);
        LocalDateTime endTime = LocalDateTime.now();
        List<Map<String, Object>> data = orderRepository.getTopCollectors(startTime, endTime, limit);
        
        List<RankingData.CollectorRanking> result = new ArrayList<>();
        for (Map<String, Object> item : data) {
            RankingData.CollectorRanking ranking = new RankingData.CollectorRanking();
            ranking.setCollectorId(((Number) item.get("collector_id")).longValue());
            ranking.setCollectorName((String) item.get("collector_name"));
            ranking.setOrderCount(((Number) item.get("count")).longValue());
            ranking.setTotalWeight(new BigDecimal(((Number) item.get("total_weight")).toString()));
            ranking.setTotalIncome(new BigDecimal(((Number) item.get("total_income")).toString()));
            ranking.setRating(((Number) item.get("average_rating")).doubleValue());
            result.add(ranking);
        }
        return result;
    }

    @Override
    public List<RegionStatistics> getRegionStatistics(String timeRange) {
        LocalDateTime startTime = getStartTimeByRange(timeRange);
        LocalDateTime endTime = LocalDateTime.now();
        List<Map<String, Object>> data = orderRepository.getRegionStatistics(startTime, endTime);
        
        List<RegionStatistics> result = new ArrayList<>();
        for (Map<String, Object> item : data) {
            RegionStatistics stats = new RegionStatistics();
            stats.setRegion((String) item.get("region"));
            stats.setOrderCount(((Number) item.get("count")).longValue());
            stats.setTotalWeight(new BigDecimal(((Number) item.get("totalWeight")).toString()));
            stats.setTotalIncome(new BigDecimal(((Number) item.get("totalIncome")).toString()));
            result.add(stats);
        }
        return result;
    }

    @Override
    public RegionStatistics getRegionDetail(String region, String timeRange) {
        LocalDateTime startTime = getStartTimeByRange(timeRange);
        LocalDateTime endTime = LocalDateTime.now();
        List<Map<String, Object>> data = orderRepository.getRegionDetail(region, startTime, endTime);
        
        RegionStatistics stats = new RegionStatistics();
        stats.setRegion(region);
        
        Map<String, Long> statusCounts = new HashMap<>();
        for (Map<String, Object> item : data) {
            String status = (String) item.get("status");
            Long count = ((Number) item.get("count")).longValue();
            statusCounts.put(status, count);
        }
        
        stats.setStatusMap(statusCounts);
        return stats;
    }

    @Override
    public Map<String, Object> getOverallStats() {
        Map<String, Object> stats = new HashMap<>();
        stats.put("totalOrders", orderRepository.count());
        stats.put("totalUsers", userRepository.count());
        stats.put("totalIncome", orderRepository.getTotalIncome());
        stats.put("totalWeight", orderRepository.sumTotalWeight());
        return stats;
    }

    @Override
    public Map<String, Object> getTrendData(int days) {
        LocalDateTime startTime = LocalDateTime.now().minusDays(days);
        LocalDateTime endTime = LocalDateTime.now();
        Map<String, Object> data = new HashMap<>();
        data.put("trend", orderRepository.getOrderTrend(startTime));
        return data;
    }

    @Override
    public Map<String, Object> getTotalStatistics() {
        Map<String, Object> stats = new HashMap<>();
        stats.put("totalWeight", orderRepository.sumTotalWeight());
        stats.put("totalIncome", orderRepository.getTotalIncome());
        return stats;
    }

    @Override
    public List<Map<String, Object>> getOrderTrend(int months) {
        LocalDateTime startTime = LocalDateTime.now().minusMonths(months);
        return orderRepository.getOrderTrend(startTime);
    }

    @Override
    public List<Map<String, Object>> getItemDistribution() {
        return orderRepository.getItemDistribution();
    }

    @Override
    public Map<String, Object> getDailyStatistics() {
        LocalDateTime startTime = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
        LocalDateTime endTime = LocalDateTime.now();
        Map<String, Object> stats = new HashMap<>();
        stats.put("orderCount", orderRepository.countByCreateTimeBetween(startTime, endTime));
        stats.put("totalWeight", orderRepository.sumWeightByDateRange(startTime, endTime));
        stats.put("totalIncome", orderRepository.sumIncomeByDateRange(startTime, endTime));
        return stats;
    }

    @Override
    public Map<String, Object> getMonthlyStatistics() {
        LocalDateTime startTime = LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
        LocalDateTime endTime = LocalDateTime.now();
        Map<String, Object> stats = new HashMap<>();
        stats.put("orderCount", orderRepository.countByCreateTimeBetween(startTime, endTime));
        stats.put("totalWeight", orderRepository.sumWeightByDateRange(startTime, endTime));
        stats.put("totalIncome", orderRepository.sumIncomeByDateRange(startTime, endTime));
        return stats;
    }

    @Override
    public StatisticsData getStatisticsData(LocalDateTime startTime, LocalDateTime endTime) {
        StatisticsData data = new StatisticsData();
        data.setOrderStats(getOrderStatistics(startTime, endTime));
        data.setCollectorStats(getCollectorStatistics(startTime, endTime));
        data.setStationStats(getStationStatistics(startTime, endTime));
        data.setAreaStats(getAreaStatistics(startTime, endTime));
        return data;
    }

    @Override
    public StatisticsData.OrderStatistics getOrderStatistics(LocalDateTime startTime, LocalDateTime endTime) {
        StatisticsData.OrderStatistics stats = new StatisticsData.OrderStatistics();
        stats.setTotalOrders(orderRepository.countByCreateTimeBetween(startTime, endTime));
        stats.setTotalWeight(orderRepository.sumWeightByDateRange(startTime, endTime));
        stats.setTotalIncome(orderRepository.sumIncomeByDateRange(startTime, endTime));
        stats.setAverageRating(orderRepository.averageRating());
        return stats;
    }

    @Override
    public StatisticsData.CollectorStatistics getCollectorStatistics(LocalDateTime startTime, LocalDateTime endTime) {
        StatisticsData.CollectorStatistics stats = new StatisticsData.CollectorStatistics();
        
        // 获取回收员总数
        stats.setTotalCollectors(userMapper.countCollectors());
        
        // 获取活跃回收员数（在指定时间范围内有订单的回收员）
        List<Map<String, Object>> activeCollectors = orderRepository.getTopCollectors(startTime, endTime, Integer.MAX_VALUE);
        stats.setActiveCollectors(activeCollectors.size());
        
        // 计算平均评分
        Double avgRating = orderRepository.averageRating();
        stats.setAvgRating(avgRating != null ? avgRating : 0.0);
        
        // 计算平均月订单数
        long totalOrders = orderRepository.countByCreateTimeBetween(startTime, endTime);
        int monthsBetween = (int) java.time.temporal.ChronoUnit.MONTHS.between(startTime, endTime);
        stats.setAvgMonthlyOrders((double) totalOrders / Math.max(1, monthsBetween));
        
        // 获取绩效排名
        stats.setTopCollectors(orderRepository.getTopCollectors(startTime, endTime, 10));
        
        return stats;
    }

    @Override
    public StatisticsData.StationStatistics getStationStatistics(LocalDateTime startTime, LocalDateTime endTime) {
        StatisticsData.StationStatistics stats = new StatisticsData.StationStatistics();
        
        // 获取回收站总数
        stats.setTotalStations(stationMapper.countTotal());
        
        // 获取活跃回收站数（在指定时间范围内有订单的回收站）
        List<Map<String, Object>> activeStations = orderRepository.getTopStations(startTime, endTime, Integer.MAX_VALUE);
        stats.setActiveStations(activeStations.size());
        
        // 计算平均日订单数
        long totalOrders = orderRepository.countByCreateTimeBetween(startTime, endTime);
        int daysBetween = (int) java.time.temporal.ChronoUnit.DAYS.between(startTime, endTime);
        stats.setAvgDailyOrders((double) totalOrders / Math.max(1, daysBetween));
        
        // 获取回收站排名
        stats.setTopStations(orderRepository.getTopStations(startTime, endTime, 10));
        
        return stats;
    }

    @Override
    public List<StatisticsData.AreaStatistics> getAreaStatistics(LocalDateTime startTime, LocalDateTime endTime) {
        List<Map<String, Object>> regionStats = orderRepository.getRegionStatistics(startTime, endTime);
        List<StatisticsData.AreaStatistics> result = new ArrayList<>();
        
        for (Map<String, Object> stat : regionStats) {
            StatisticsData.AreaStatistics areaStats = new StatisticsData.AreaStatistics();
            areaStats.setArea((String) stat.get("region"));
            areaStats.setOrderCount(((Number) stat.get("count")).longValue());
            areaStats.setTotalWeight(new BigDecimal(((Number) stat.get("totalWeight")).toString()));
            
            // 获取区域内的回收站数量
            areaStats.setStationCount(stationMapper.countByRegion((String) stat.get("region")));
            
            // 获取区域平均评分
            Double avgRating = orderRepository.averageRating();
            areaStats.setAvgRating(avgRating != null ? avgRating : 0.0);
            
            // 获取区域用户数量
            areaStats.setUserCount(userMapper.countByRegion((String) stat.get("region")));
            
            // 获取区域趋势数据
            areaStats.setTrendData(orderRepository.getRegionTrend((String) stat.get("region"), startTime, endTime));
            
            result.add(areaStats);
        }
        
        return result;
    }

    @Override
    public List<Map<String, Object>> getOrderTrend(LocalDateTime startTime, LocalDateTime endTime, int points) {
        return orderRepository.getOrderTrend(startTime);
    }

    @Override
    public List<Map<String, Object>> getItemDistribution(LocalDateTime startTime, LocalDateTime endTime) {
        return orderRepository.getItemDistributionByDateRange(startTime, endTime);
    }

    @Override
    public List<Map<String, Object>> getTopCollectors(LocalDateTime startTime, LocalDateTime endTime, int limit) {
        return orderRepository.getTopCollectors(startTime, endTime, limit);
    }

    @Override
    public List<Map<String, Object>> getTopStations(LocalDateTime startTime, LocalDateTime endTime, int limit) {
        return orderRepository.getTopStations(startTime, endTime, limit);
    }

    @Override
    public List<Map<String, Object>> getTopAreas(LocalDateTime startTime, LocalDateTime endTime, int limit) {
        return orderRepository.getRegionStatistics(startTime, endTime);
    }

    @Override
    public ChartData getRegionTrend(String region, String timeRange) {
        LocalDateTime startTime = getStartTimeByRange(timeRange);
        LocalDateTime endTime = LocalDateTime.now();
        List<Map<String, Object>> data = orderRepository.getRegionTrend(region, startTime, endTime);
        
        ChartData chartData = new ChartData();
        List<String> labels = new ArrayList<>();
        List<Double> values = new ArrayList<>();
        
        for (Map<String, Object> item : data) {
            labels.add(item.get("date").toString());
            values.add(((Number) item.get("count")).doubleValue());
        }
        
        chartData.setLabels(labels);
        chartData.setValues(values);
        
        return chartData;
    }

    private LocalDateTime getStartTimeByRange(String timeRange) {
        LocalDateTime now = LocalDateTime.now();
        switch (timeRange.toLowerCase()) {
            case "day":
                return now.minusDays(1);
            case "week":
                return now.minusWeeks(1);
            case "month":
                return now.minusMonths(1);
            case "year":
                return now.minusYears(1);
            default:
                return now.minusMonths(1);
        }
    }

    private int getDaysByRange(String timeRange) {
        switch (timeRange.toLowerCase()) {
            case "today":
                return 1;
            case "week":
                return 7;
            case "month":
                return 30;
            case "year":
                return 365;
            default:
                return 30;
        }
    }

    private double calculateGrowthRate(Double previous, Double current) {
        if (previous == null || previous == 0) {
            return current == null || current == 0 ? 0 : 100;
        }
        if (current == null) {
            return -100;
        }
        return ((current - previous) / previous) * 100;
    }
} 